package com.kiki.oauth.config;

import cn.hutool.core.io.IoUtil;
import com.kiki.oauth.convert.CustomerAccessTokenConverter;
import com.kiki.oauth.enums.AuthTokenTypeEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.io.IOException;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * 资源服务器的职责
 *  token的校验
 *  给与资源
 */
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    private final String REDIS_STORE_PREFIX = "oauth:";

    @Autowired
    private DataSource dataSource;

    @Autowired
    private SecurityProperties securityProperties;

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    @Autowired
    private UserDetailsService userDetailsService;

    /**
     * TokenService配置：在不采用JWT的情况下，需要配置RemoteTokenServices来充当tokenServices，它主要完成Token的校验等工作。因此需要指定校验Token的授权服务器接口地址
     * @return
     */
    /*@Bean
    public RemoteTokenServices remoteTokenServices() {
        final RemoteTokenServices tokenServices = new RemoteTokenServices();
        tokenServices.setClientId("client");
        tokenServices.setClientSecret("secret");
        tokenServices.setCheckTokenEndpointUrl("http://localhost:8080/oauth/check_token");
        return tokenServices;
    }*/

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.stateless(true);
        resources.tokenStore(tokenStore());
    }

    @Bean
    public TokenStore tokenStore() {
        String tokenType = securityProperties.getType();
        if(StringUtils.isEmpty(tokenType)){
            //默认是jdbc类型
            tokenType = AuthTokenTypeEnum.JDBC.getType();
        }
        TokenStore tokenStore = null;
        if (AuthTokenTypeEnum.JWT.getType().equals(tokenType)) {
            tokenStore = new JwtTokenStore(jwtAccessTokenConverter());
        } else if (AuthTokenTypeEnum.JDBC.getType().equals(tokenType)) {
            tokenStore = new JdbcTokenStore(dataSource);
        } else if (AuthTokenTypeEnum.REDIS.getType().equals(tokenType)) {
            tokenStore = new RedisTokenStore(redisConnectionFactory);
            ((RedisTokenStore)tokenStore).setPrefix(REDIS_STORE_PREFIX);
        }else{
            throw new RuntimeException("error token store type "+tokenType);
        }
        return tokenStore;
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        //session创建策略
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
        //所有请求需要认证
        http.authorizeRequests().anyRequest().authenticated();
    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        //对称加密
        //jwtAccessTokenConverter.setSigningKey("123");
        jwtAccessTokenConverter.setVerifierKey(getPublicKeyAsString());

        //为了能获取完整的用户信息
        jwtAccessTokenConverter.setAccessTokenConverter(new CustomerAccessTokenConverter());

        return jwtAccessTokenConverter;
    }

    private String getPublicKeyAsString() {
        try {
            return IoUtil.read(securityProperties.getJwt().getPublicKey().getInputStream(), UTF_8);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}
